import shape from '../shape'
import sequence from './serialNumber'
import { getLatextSize } from './katex'

/**
 * 主题节点的宽高计算
 * @param {*} nodes 主题结合
 * @param {*} aligning 是否同主题对齐
 * @param {*} compact 是否紧凑布局
 * @param {*} skeletonTheme 当前画布主题样式
 */
export function calculateTopicsSize (nodes, compact, aligning, skeletonTheme) {
  nodes.forEach(node => {
    const { text, link, topiclink, comment, latex, tag, imageInfo, targetSummarys, isTask, marks } = node.data
    const { fontSize, fontWeight, maxWidth: maxW, fontFamily, fontStyle, textDirection } = node.style.textStyle
    const maxWidth = textDirection === 'ver' ? undefined : `${maxW}px`
    let { width, height } = getTopicNodeRect({ text: !imageInfo && !latex ? text || '0' : text, fontSize, fontWeight, fontFamily, fontStyle, maxWidth, textDirection })
    const { width: latexWidth, height: latexHeight } = latex ? getLatextSize(fontSize, latex) : { width: 0, height: 0 }
    width += latexWidth
    height = Math.max(height, latexHeight)
    node.data.foreignObjectHeight = height
    if (imageInfo) {
      const { width: imageWidth, height: imageHeight } = node.data.imageInfo
      width = Math.max(width, imageWidth)
      height = height + imageHeight + (node.data.text ? 8 : 0)
    }
    node.data.foreignObjectWidth = width
    if (marks?.length) {
      node.markWidth = node.data.marks.length * node.style.markSize - (node.data.marks.length - 1) * node.style.markSize * 0.15 + 6
      width = width + node.markWidth
      height = Math.max(height, node.style.markSize)
    }
    if (link) {
      width += node.style.linkSize + 8
    }
    if (comment) {
      width += node.style.linkSize + 8
    }
    if (topiclink) {
      width += node.style.linkSize + 8
    }
    if (targetSummarys?.length) {
      for (let i = 0; i < targetSummarys.length; i++) {
        targetSummarys[i].style = {
          ...skeletonTheme.summaryStyle,
          ...targetSummarys[i].style
        }
        const { fontSize, fontFamily, topicShape } = targetSummarys[i].style
        const { width, height } = getTopicNodeRect({ text: targetSummarys[i].text, fontSize, fontWeight: 'bold', fontFamily, maxWidth: '620px' })
        const shapeGetPadding = shape[topicShape].shapeGetPadding
        const { paddingT = 6, paddingL = 10 } = shapeGetPadding ? shapeGetPadding(width, height, 10, 10) : {}
        targetSummarys[i].paddingLR = paddingL
        targetSummarys[i].paddingTB = paddingT
        targetSummarys[i].summaryWidth = width + targetSummarys[i].paddingLR * 2
        targetSummarys[i].summaryHeight = height + targetSummarys[i].paddingTB * 2
      }
    }
    if (isTask) {
      width = width + node.style.linkSize + 8
    }
    if (node.parent?.data['child-serial']) {
      const childSerialType = node.parent?.data['child-serial']
      const childSerialTypeSplit = childSerialType.split('-')
      const parent = node.parent
      const brothers = parent.children
      const idx = brothers.findIndex(o => o.data._id === node.data._id)
      const serialNumber = sequence[childSerialTypeSplit[0]](idx + 1, childSerialTypeSplit[1])
      const { width: serialNumberWidth, height: serialNumberHeight } = getTopicNodeRect({ text: `${serialNumber}`, fontSize, fontWeight, fontFamily, fontStyle })
      width += serialNumberWidth
      node.serialNumberWidth = serialNumberWidth
      node.serialNumberHeight = serialNumberHeight
      node.serialNumber = serialNumber
    }
    if (tag) {
      const tags = node.data.tag.split(';').filter(Boolean)
      const tagsInfo = []
      const tagLength = tags.reduce((prev, cur) => {
        const tagClientRect = getTopicNodeRect({
          text: cur,
          fontSize: node.style.tagSize
        })
        const tagWidth = tagClientRect.width + 12
        const tagHeight = tagClientRect.height + 6
        tagsInfo.push({ tagName: cur, tagWidth, tagHeight, prevWidth: prev })
        prev += tagWidth
        return prev
      }, 0) + (tags.length + 1) * 8
      width = Math.max(tagLength, width)
      node.data.tagsInfo = tagsInfo
      node.tagLineHeight = tagsInfo[0].tagHeight + 6
      node.tagLength = tagLength
      height += node.tagLineHeight
    } else {
      node.data.tagsInfo = []
      node.tagLineHeight = 0
    }
    const shapeGetPadding = shape[node.style?.shape || 'default'].shapeGetPadding
    const { paddingT = 0, paddingR = 0, paddingB = 0, paddingL = 0 } = shapeGetPadding ? shapeGetPadding(width, height) : {}
    node.style.margin._t = paddingT || node.style.margin._t
    node.style.margin._r = paddingR || node.style.margin._r
    node.style.margin._b = paddingB || node.style.margin._b
    node.style.margin._l = paddingL || node.style.margin._l
    if (compact) {
      for (const key in node.style.margin) {
        if (!['heart', 'boom', 'flower'].includes(node.style.shape)) {
          node.style.margin[key] = node.style.margin[key] / 1.5
        }
      }
    }
    node.width = width + node.style.margin._r + node.style.margin._l
    node.height = height + node.style.margin._t + node.style.margin._b
  })
  aligning && sameWidthWithSameDepth(nodes)
}

/**
 * 同级节点对齐
 * @param {*} nodes
 */
export function sameWidthWithSameDepth (nodes) {
  const maxDepth = Math.max(...nodes.map(o => o.depth))
  const cache = new Array(maxDepth).fill(null).map(() => [])
  nodes.forEach(n => {
    if (n.depth > 0) {
      cache[n.depth - 1].push(n)
    }
  })
  const maxWidths = cache.map(o => (Math.max(...o.map(k => k.width))))
  for (let i = 0; i < cache.length; i++) {
    const c = cache[i]
    for (let k = 0; k < c.length; k++) {
      if (maxWidths[i] !== c[k].width) {
        c[k].data.foreignObjectWidth += maxWidths[i] - c[k].width
        c[k].width = maxWidths[i]
      }
    }
  }
}

/**
 * 长文本换行后文本宽高获取
 * @param {*} options
 */
export function getTopicNodeRect (options) {
  const {
    text,
    fontSize,
    fontWeight,
    fontFamily,
    fontStyle = 'normal',
    maxWidth,
    textDirection,
    padding,
    wordBreak,
    writingMode,
    lineBreak,
    transform
  } = options
  const textSpan = document.createElement('p')
  const spanStyle = {
    maxWidth,
    fontSize: fontSize + 'px',
    fontWeight,
    fontFamily,
    fontStyle,
    whiteSpace: 'pre-wrap',
    display: 'inline-block',
    position: 'fixed',
    left: '-2000px',
    padding,
    wordBreak: wordBreak || (textDirection === 'ver' ? 'keep-all' : 'break-all'),
    lineBreak: lineBreak || (textDirection === 'ver' ? 'auto' : 'anywhere'),
    writingMode: writingMode || (textDirection === 'ver' ? 'vertical-lr' : 'horizontal-tb'),
    transform
  }
  for (const key in spanStyle) {
    textSpan.style[key] = spanStyle[key]
  }
  textSpan.innerText = text
  document.body.append(textSpan)
  const { width, height } = textSpan.getBoundingClientRect()
  textSpan.remove()
  return {
    width: fontStyle === 'italic' ? width + 2 : width,
    height
  }
}
